var __createBinding =
    (this && this.__createBinding) ||
    (Object.create
        ? (o, m, k, k2) => {
              if (k2 === undefined) k2 = k;
              var desc = Object.getOwnPropertyDescriptor(m, k);
              if (
                  !desc ||
                  ("get" in desc
                      ? !m.__esModule
                      : desc.writable || desc.configurable)
              ) {
                  desc = { enumerable: true, get: () => m[k] };
              }
              Object.defineProperty(o, k2, desc);
          }
        : (o, m, k, k2) => {
              if (k2 === undefined) k2 = k;
              o[k2] = m[k];
          });
var __setModuleDefault =
    (this && this.__setModuleDefault) ||
    (Object.create
        ? (o, v) => {
              Object.defineProperty(o, "default", {
                  enumerable: true,
                  value: v,
              });
          }
        : (o, v) => {
              o.default = v;
          });
var __importStar =
    (this && this.__importStar) ||
    ((mod) => {
        if (mod?.__esModule) return mod;
        var result = {};
        if (mod != null)
            for (var k in mod)
                if (k !== "default" && Object.hasOwn(mod, k))
                    __createBinding(result, mod, k);
        __setModuleDefault(result, mod);
        return result;
    });
var __importDefault =
    (this && this.__importDefault) ||
    ((mod) => (mod?.__esModule ? mod : { default: mod }));
Object.defineProperty(exports, "__esModule", { value: true });
exports.ObjectPacker = void 0;
const t = __importStar(require("@babel/types"));
const variable_1 = require("../../helpers/variable");
const transformation_1 = require("../transformation");
const traverse_1 = __importDefault(require("@babel/traverse"));
class ObjectPacker extends transformation_1.Transformation {
    /**
     * Executes the transformation.
     * @param log The log function.
     */
    execute(_log) {
        const self = this;
        (0, traverse_1.default)(this.ast, {
            enter(path) {
                const variable = (0, variable_1.findConstantVariable)(
                    path,
                    isEmptyObjectExpression,
                );
                if (!variable) {
                    return;
                }
                const parentPath = path.getStatementParent();
                if (
                    !parentPath ||
                    parentPath.parentPath === undefined ||
                    typeof parentPath.key !== "number"
                ) {
                    return;
                }
                const statements =
                    parentPath.parentPath.node[parentPath.parentKey];
                let numRemoved = 0;
                for (let i = parentPath.key + 1; i < statements.length; i++) {
                    const node = statements[i];
                    if (
                        t.isExpressionStatement(node) &&
                        self.isPropertyAssignment(
                            node.expression,
                            variable.name,
                        )
                    ) {
                        // replace multiple properties assigned in same statement
                        if (
                            self.isPropertyAssignment(
                                node.expression.right,
                                variable.name,
                            )
                        ) {
                            const properties = [node.expression.left];
                            let right = node.expression.right;
                            while (
                                self.isPropertyAssignment(right, variable.name)
                            ) {
                                properties.push(right.left);
                                right = right.right;
                            }
                            // don't duplicate expressions with side effects
                            if (!t.isLiteral(right)) {
                                return;
                            }
                            for (const { property } of properties) {
                                const isComputed =
                                    !t.isStringLiteral(property) &&
                                    !t.isNumericLiteral(property) &&
                                    !t.isIdentifier(property);
                                const objectProperty = t.objectProperty(
                                    property,
                                    right,
                                    isComputed,
                                );
                                variable.expression.properties.push(
                                    objectProperty,
                                );
                                self.setChanged();
                                numRemoved++;
                            }
                        } else {
                            const key = node.expression.left.property;
                            const isComputed =
                                !t.isStringLiteral(key) &&
                                !t.isNumericLiteral(key) &&
                                !t.isIdentifier(key);
                            const property = t.objectProperty(
                                key,
                                node.expression.right,
                                isComputed,
                            );
                            variable.expression.properties.push(property);
                            self.setChanged();
                            numRemoved++;
                        }
                    } else {
                        break;
                    }
                }
                statements.splice(parentPath.key + 1, numRemoved);
            },
        });
        return this.hasChanged();
    }
    /**
     * Returns whether a node is setting a property on a given object.
     * @param node The AST node.
     * @param objectName The name of the object.
     * @returns Whether.
     */
    isPropertyAssignment(node, objectName) {
        return (
            t.isAssignmentExpression(node) &&
            t.isMemberExpression(node.left) &&
            t.isIdentifier(node.left.object) &&
            node.left.object.name === objectName
        );
    }
}
exports.ObjectPacker = ObjectPacker;
ObjectPacker.properties = {
    key: "objectPacking",
};
/**
 * Returns whether a node is an empty object expression.
 * @param node The node.
 * @returns Whether.
 */
const isEmptyObjectExpression = (node) => {
    return t.isObjectExpression(node) && node.properties.length === 0;
};
